<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Unit tests for the EventUtils class</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-git.css" type="text/css" />
<script src="http://code.jquery.com/qunit/qunit-git.js"></script>
<script src="qunit/connector.js"></script>
<script type="text/javascript" src="qunit/runner.js"></script>
<script type="text/javascript" src="js/tiny_mce_loader.js"></script>
</head>
<body>
<script type="text/javascript">
var eventUtils = tinymce.dom.Event;

QUnit.config.reorder = false;

module("tinymce.dom.Event", {
	teardown: function() {
		eventUtils.clean(window);
	}
});

// Bind dummy ready so it gets the domLoaded ready state
eventUtils.bind(window, "ready", function() {});

test("unbind all", function() {
	var result;

	eventUtils.bind(window, 'click', function(e) {
		result.click = true;
	});

	eventUtils.bind(window, 'keydown', function(e) {
		result.keydown1 = true;
	});

	eventUtils.bind(window, 'keydown', function(e) {
		result.keydown2 = true;
	});

	result = {};
	eventUtils.fire(window, 'click');
	eventUtils.fire(window, 'keydown');
	deepEqual(result, {click: true, keydown1: true, keydown2: true});

	eventUtils.unbind(window);
	result = {};
	eventUtils.fire(window, 'click');
	eventUtils.fire(window, 'keydown');
	deepEqual(result, {});
});

test("unbind event", function() {
	var result;

	eventUtils.bind(window, 'click', function(e) {
		result.click = true;
	});

	eventUtils.bind(window, 'keydown', function(e) {
		result.keydown1 = true;
	});

	eventUtils.bind(window, 'keydown', function(e) {
		result.keydown2 = true;
	});

	result = {};
	eventUtils.fire(window, 'click');
	eventUtils.fire(window, 'keydown');
	deepEqual(result, {click: true, keydown1: true, keydown2: true});

	eventUtils.unbind(window, 'click');
	result = {};
	eventUtils.fire(window, 'click');
	eventUtils.fire(window, 'keydown');
	deepEqual(result, {keydown1: true, keydown2: true});
});

test("unbind event non existing", function() {
	eventUtils.unbind(window, 'noevent');
	ok(true, "No exception");
});

test("unbind callback", function() {
	var result;

	eventUtils.bind(window, 'click', function(e) {
		result.click = true;
	});

	eventUtils.bind(window, 'keydown', function(e) {
		result.keydown1 = true;
	});

	function callback2(e) {
		result.keydown2 = true;
	};

	eventUtils.bind(window, 'keydown', callback2);

	result = {};
	eventUtils.fire(window, 'click');
	eventUtils.fire(window, 'keydown');
	deepEqual(result, {click: true, keydown1: true, keydown2: true});

	eventUtils.unbind(window, 'keydown', callback2);
	result = {};
	eventUtils.fire(window, 'click');
	eventUtils.fire(window, 'keydown');
	deepEqual(result, {click: true, keydown1: true});
});

test("unbind multiple", function() {
	var result;

	eventUtils.bind(window, 'mouseup mousedown click', function(e) {
		result[e.type] = true;
	});

	eventUtils.unbind(window, 'mouseup mousedown');

	result = {};
	eventUtils.fire(window, 'mouseup');
	eventUtils.fire(window, 'mousedown');
	eventUtils.fire(window, 'click');
	deepEqual(result, {click: true});
});

test("bind multiple", function() {
	var result;

	eventUtils.bind(window, 'mouseup mousedown', function(e) {
		result[e.type] = true;
	});

	result = {};
	eventUtils.fire(window, 'mouseup');
	eventUtils.fire(window, 'mousedown');
	eventUtils.fire(window, 'click');
	deepEqual(result, {mouseup: true, mousedown: true});
});

test("bind/fire bubbling", function() {
	var result;

	eventUtils.bind(window, 'click', function(e) {
		result.window = true;
	});

	eventUtils.bind(document, 'click', function() {
		result.document = true;
	});

	eventUtils.bind(document.body, 'click', function() {
		result.body = true;
	});

	eventUtils.bind(document.getElementById('content'), 'click', function() {
		result.content = true;
	});

	eventUtils.bind(document.getElementById('inner'), 'click', function() {
		result.inner = true;
	});

	result = {};
	eventUtils.fire(window, 'click');
	deepEqual(result, {window: true});

	result = {};
	eventUtils.fire(document, 'click');
	deepEqual(result, {document: true, window: true});

	result = {};
	eventUtils.fire(document.body, 'click');
	deepEqual(result, {body: true, document: true, window: true});

	result = {};
	eventUtils.fire(document.getElementById('content'), 'click');
	deepEqual(result, {content: true, body: true, document: true, window: true});

	result = {};
	eventUtils.fire(document.getElementById('inner'), 'click');
	deepEqual(result, {inner: true, content: true, body: true, document: true, window: true});
});

test("bind/fire stopImmediatePropagation", function() {
	var result;

	eventUtils.bind(window, 'click', function(e) {
		result.click1 = true;
	});

	eventUtils.bind(window, 'click', function(e) {
		result.click2 = true;
		e.stopImmediatePropagation();
	});

	eventUtils.bind(window, 'click', function(e) {
		result.click3 = true;
	});

	result = {};
	eventUtils.fire(window, 'click');
	deepEqual(result, {click1: true, click2: true});
});

test("bind/fire stopPropagation", function() {
	var result;

	eventUtils.bind(window, 'click', function(e) {
		result.click1 = true;
	});

	eventUtils.bind(document.body, 'click', function(e) {
		result.click2 = true;
	});

	eventUtils.bind(document.getElementById('inner'), 'click', function(e) {
		result.click3 = true;
		e.stopPropagation();
	});

	result = {};
	eventUtils.fire(document.getElementById('inner'), 'click');
	deepEqual(result, {click3: true});
});

test("clean window", function() {
	var result;

	eventUtils.bind(window, 'click', function(e) {
		result.click1 = true;
	});

	eventUtils.bind(document.body, 'click', function(e) {
		result.click2 = true;
	});

	eventUtils.bind(document.getElementById('content'), 'click', function(e) {
		result.click3 = true;
	});

	eventUtils.bind(document.getElementById('inner'), 'click', function(e) {
		result.click4 = true;
	});

	result = {};
	eventUtils.fire(document.getElementById('inner'), 'click');
	deepEqual(result, {click1: true, click2: true, click3: true, click4: true});

	eventUtils.clean(window);
	result = {};
	eventUtils.fire(document.getElementById('inner'), 'click');
	deepEqual(result, {});
});

test("clean document", function() {
	var result;

	eventUtils.bind(window, 'click', function(e) {
		result.click1 = true;
	});

	eventUtils.bind(document, 'click', function(e) {
		result.click2 = true;
	});

	eventUtils.bind(document.body, 'click', function(e) {
		result.click3 = true;
	});

	eventUtils.bind(document.getElementById('content'), 'click', function(e) {
		result.click4 = true;
	});

	eventUtils.bind(document.getElementById('inner'), 'click', function(e) {
		result.click5 = true;
	});

	result = {};
	eventUtils.fire(document.getElementById('inner'), 'click');
	deepEqual(result, {click1: true, click2: true, click3: true, click4: true, click5: true});

	eventUtils.clean(document);
	result = {};
	eventUtils.fire(document.getElementById('inner'), 'click');
	deepEqual(result, {click1: true});
});

test("clean element", function() {
	var result;

	eventUtils.bind(window, 'click', function(e) {
		result.click1 = true;
	});

	eventUtils.bind(document.body, 'click', function(e) {
		result.click2 = true;
	});

	eventUtils.bind(document.getElementById('content'), 'click', function(e) {
		result.click3 = true;
	});

	eventUtils.bind(document.getElementById('inner'), 'click', function(e) {
		result.click4 = true;
	});

	result = {};
	eventUtils.fire(document.getElementById('inner'), 'click');
	deepEqual(result, {click1: true, click2: true, click3: true, click4: true});

	eventUtils.clean(document.getElementById('content'));
	result = {};
	eventUtils.fire(document.getElementById('inner'), 'click');
	deepEqual(result, {click1: true, click2: true});
});

test("mouseenter/mouseleave bind/unbind", function() {
	var result = {};

	eventUtils.bind(document.body, 'mouseenter mouseleave', function(e) {
		result[e.type] = true;
	});

	eventUtils.fire(document.body, 'mouseenter');
	eventUtils.fire(document.body, 'mouseleave');

	deepEqual(result, {mouseenter: true, mouseleave: true});

	result = {};
	eventUtils.clean(document.body);
	eventUtils.fire(document.body, 'mouseenter');
	eventUtils.fire(document.body, 'mouseleave');
	deepEqual(result, {});
});

test("focusin/focusout bind/unbind", function() {
	var result = {};

	eventUtils.bind(document.body, 'focusin focusout', function(e) {
		// IE will fire a focusout on the parent element if you focus an element within not a big deal so lets detect it in the test
		if (e.type == "focusout" && e.target.contains(document.activeElement)) {
			return;
		}

		result[e.type] = result[e.type] ? ++result[e.type] : 1;
	});

	document.getElementById('inner').focus();
	document.getElementById('content').focus();

	deepEqual(result, {focusin: 2, focusout: 1});
});

test("bind unbind fire clean on null", function() {
	eventUtils.bind(null, 'click', function() {});
	eventUtils.unbind(null, 'click', function() {});
	eventUtils.fire(null, {});
	eventUtils.clean(null);
	ok(true, "No exception");
});

test("bind ready when page is loaded", function() {
	var ready;

	eventUtils.bind(window, 'ready', function() {ready = true;});

	ok(ready, "Window is ready.");
});

test("event states when event object is fired twice", function() {
	var result = {};

	eventUtils.bind(window, 'keydown', function(e) {result[e.type] = true;e.preventDefault();e.stopPropagation();});
	eventUtils.bind(window, 'keyup', function(e) {result[e.type] = true;e.stopImmediatePropagation();});

	var event = {};
	eventUtils.fire(window, 'keydown', event);
	eventUtils.fire(window, 'keyup', event);	

	ok(event.isDefaultPrevented(), "Default is prevented.");
	ok(event.isPropagationStopped(), "Propagation is stopped.");
	ok(event.isImmediatePropagationStopped(), "Immediate propagation is stopped.");

	deepEqual(result, {keydown: true, keyup: true});
});

test("unbind inside callback", function() {
	function a() {}

	eventUtils.bind(window, 'click', function() {
		eventUtils.unbind(window, 'click', a);
	});

	eventUtils.bind(window, 'click', a);
	eventUtils.fire(window, 'click', {});

	ok(true, "No exception");
});

/**
 * Fakes a mouse event.
 *
 * @param {Element/String} e DOM element object or element id to send fake event to.
 * @param {String} na Event name to fake like "click".
 * @param {Object} o Optional object with data to send with the event like cordinates.
 */
function fakeMouseEvent(e, na, o) {
	var ev;

	o = tinymce.extend({
		screenX : 0,
		screenY : 0,
		clientX : 0,
		clientY : 0
	}, o);

	e = tinymce.DOM.get(e);

	if (document.createEvent) {
		ev = document.createEvent('MouseEvents');

		if (ev.initMouseEvent)
			ev.initMouseEvent(na, true, true, window, 1, o.screenX, o.screenY, o.clientX, o.clientY, false, false, true, false, 0, null);

		e.dispatchEvent(ev);
	} else {
		if (e.fireEvent) {
			ev = document.createEventObject();
			tinymce.extend(ev, o);
			e.fireEvent('on' + na, ev);
			return;
		}
	}
};

/**
 * Fakes a key event.
 *
 * @param {Element/String} e DOM element object or element id to send fake event to.
 * @param {String} na Event name to fake like "keydown".
 * @param {Object} o Optional object with data to send with the event like keyCode and charCode.
 */
function fakeKeyEvent(e, na, o) {
	var ev;

	o = tinymce.extend({
		keyCode : 13,
		charCode : 0
	}, o);

	e = tinymce.DOM.get(e);

	if (document.createEvent) {
		try {
			// Fails in Safari
			ev = document.createEvent('KeyEvents');
			ev.initKeyEvent(na, true, true, window, false, false, false, false, o.keyCode, o.charCode);
		} catch (ex) {
			ev = document.createEvent('Events');
			ev.initEvent(na, true, true);

			ev.keyCode = o.keyCode;
			ev.charCode = o.charCode;
		}
	} else {
		if (e.fireEvent) {
			ev = document.createEventObject();
			tinymce.extend(ev, o);
			e.fireEvent('on' + na, ev);
			return;
		}

		ev = document.createEvent('UIEvents');

		if (ev.initUIEvent)
			ev.initUIEvent(na, true, true, window, 1);

		ev.keyCode = o.keyCode;
		ev.charCode = o.charCode;
	}

	e.dispatchEvent(ev);
};

(function() {
	var Event = tinymce.dom.Event, DOM = new tinymce.dom.DOMUtils(document, {keep_values : true});

	test('addRemove', 2, function() {
		var c = 0;

		DOM.add(document.body, 'div', {id : 'test'});
		DOM.get('test').innerHTML = '<span id="test2"></span>';

		function mouseCheck(e) {
			if (e.clientX == 10 || e.clientX == 101)
				c++;
		};

		function mouseCheckCancel(e) {
			if (e.clientX == 101)
				return Event.cancel(e);
		};

		function keyCheck(e) {
			if (e.keyCode == 13)
				c++;
		};

		Event.add('test2', 'click', mouseCheckCancel);
		Event.add('test', 'click', mouseCheck);
		fakeMouseEvent('test', 'click', {clientX : 10});
		fakeMouseEvent('test2', 'click', {clientX : 101});

		Event.add('test', 'keydown', keyCheck);
		fakeKeyEvent('test', 'keydown', {keyCode : 13});

		equal(c, 2, 'Event count doesn\'t add up.', tinymce.isWebKit);

		c = 0;
		Event.remove('test', 'click', mouseCheck);
		Event.remove('test', 'keydown', keyCheck);
		fakeMouseEvent('test', 'click', {clientX : 10});
		fakeKeyEvent('test', 'keydown', {keyCode : 13});

		equal(c, 0, 'Event count doesn\'t add up.', tinymce.isWebKit);

		DOM.remove('test');
	});
})();
</script>

	<h1 id="qunit-header">Unit tests for DOM Selection IE implementation</h1>
	<h2 id="qunit-banner"></h2>
	<div id="qunit-testrunner-toolbar"></div>
	<h2 id="qunit-userAgent"></h2>
	<ol id="qunit-tests"></ol>
	<div id="content" tabindex="0">
		<div id="inner" tabindex="0"></div>
	</div>
</body>
</html>
